﻿using System;
using System.Drawing;
using System.Collections.Generic;
using System.Xml;
using ApiBetaSeries.Factories;

namespace ApiBetaSeries.Model
{
    /// <summary>
    /// Represent an User on api
    /// </summary>
    internal class User : ApiBetaSeries.IUser
    {
        /// <summary>
        /// Empty Constructor
        /// </summary>
        public User() { }

        #region Attributes
        /// <summary>
        /// Login of the member
        /// </summary>
        public string Login { get; set; }
        /// <summary>
        /// date of the last cached data
        /// </summary>
        public DateTime Cached { get; set; }

        private string m_urlAvatar;
        /// <summary>
        /// Url to the avatar
        /// </summary>
        public string UrlAvatar
        {
            get { return m_urlAvatar; }
            set
            {
                m_urlAvatar = value;
                m_avatar = null;
            }
        }

        private Bitmap m_avatar = null;
        /// <summary>
        /// Avatar ot the member
        /// </summary>
        public Bitmap Avatar
        {
            get
            {
                if (m_avatar == null && !string.IsNullOrEmpty(UrlAvatar))
                {
                    m_avatar = new Bitmap(UrlAvatar);
                }
                return m_avatar;
            }
        }
        /// <summary>
        /// number of shows followed
        /// </summary>
        public int ShowNumber { get; set; }
        /// <summary>
        /// number of seasons
        /// </summary>
        public int SeasonNumber { get; set; }
        /// <summary>
        /// number of episodes
        /// </summary>
        public int EpisodeNumber { get; set; }
        /// <summary>
        /// general progress
        /// </summary>
        public string Progress { get; set; }
        /// <summary>
        /// number of episodes to watch
        /// </summary>
        public int EpisodeToWatch { get; set; }
        /// <summary>
        /// time spended watching shows
        /// </summary>
        public TimeSpan TimeOnTv { get; set; }
        /// <summary>
        /// remaining time to spend
        /// </summary>
        public TimeSpan TimeToSpend { get; set; }

        public List<IShowUser> Shows { get; set; }

        public string Token { get; set; }
        #endregion Attributes

        #region Methods
        /// <summary>
        /// get all episodes of the member
        /// </summary>
        /// <param name="type">VO/VOVF/All</param>
        /// <param name="next">Only the next episode to watch</param>
        /// <returns>list of episode</returns>
        [ApiBetaSeries.Version.ApiMethod(true, "members/episodes")]
        public List<IEpisode> GetEpisodes(TypeEpisode type = TypeEpisode.All, bool next = false)
        {
            List<IEpisode> list = new List<IEpisode>();
            string stype = type.ToString().ToLower();
            string url = string.Format("/members/episodes/{0}.xml", stype);
            string param = string.Format("&token={0}", this.Token);
            if (next)
                param += "&view=next";

            XmlDocument doc = Utils.UtilsXML.GetUrl(url, param);
            XmlElement xe = doc["root"]["episodes"];

            if (xe == null)
            {
                throw new BetaSeriesException(1003, "Invalid action.");
            }

            foreach (XmlElement e in xe.GetElementsByTagName("episode"))
            {
                // there is an episode inside episode...
                if (e.ParentNode.Name.Equals("episode"))
                    continue;
                IEpisode episode = EpisodeFactory.CreateEpisode(e); ;
                list.Add(episode);
            }


            return list;
        }


        /// <summary>
        /// If the member has watched this episode
        /// </summary>
        /// <param name="episode">episode to check</param>
        /// <returns>true if the member has already watched this episode</returns>
        [ApiBetaSeries.Version.ApiMethod(false, "shows/episodes")]
        public bool HasWatched(IEpisode episode)
        {
            return HasWatched(episode.UrlShow, episode.Season, episode.Number);
        }
        /// <summary>
        /// If the member has watched this episode
        /// </summary>
        /// <param name="urlShow">url of the show</param>
        /// <param name="season">season of the episode</param>
        /// <param name="episode">episode number</param>
        /// <returns>true if the member has already watched this episode</returns>
        [ApiBetaSeries.Version.ApiMethod(true, "shows/episodes")]
        public bool HasWatched(string urlShow, int season, int episode)
        {
            string urlApi = string.Format("/shows/episodes/{0}.xml", urlShow);
            string param = string.Format("&season={0}&episode={1}&token={2}", season, episode, this.Token);

            XmlDocument doc = Utils.UtilsXML.GetUrl(urlApi, param);
            XmlElement s = doc["root"]["seasons"]["season"];

            if (s == null)
            {
                throw new BetaSeriesException(4005, "This season doesn't exist.");
            }

            XmlNode e = s.GetElementsByTagName("episode")[0];

            return Utils.UtilsXML.GetBoolValue(e, "has_seen"); ;
        }



        /// <summary>
        /// Add a show to the member
        /// </summary>
        /// <param name="urlSerie">url of the show</param>
        [ApiBetaSeries.Version.ApiMethod(true, "shows/add")]
        public void AddShow(string urlSerie)
        {
            string url = string.Format("/shows/add/{0}.xml", urlSerie);
            string param = string.Format("&token={0}", this.Token);
            XmlDocument doc = Utils.UtilsXML.GetUrl(url, param);
        }

        /// <summary>
        /// Add a show to the member
        /// </summary>
        /// <param name="show">the show</param>
        [ApiBetaSeries.Version.ApiMethod(false, "shows/add")]
        public void AddShow(IShow show)
        {
            AddShow(show.Url);
        }

        /// <summary>
        /// remove a show from a member
        /// </summary>
        /// <param name="urlSerie">url of the show</param>
        [ApiBetaSeries.Version.ApiMethod(true,"shows/remove")]
        public void DelShow(string urlSerie)
        {
            string url = string.Format("/shows/remove/{0}.xml", urlSerie);
            string param = string.Format("&token={0}", this.Token);
            XmlDocument doc = Utils.UtilsXML.GetUrl(url, param);
        }

        /// <summary>
        /// remove a show from a member
        /// </summary>
        /// <param name="show">the show</param>
        [ApiBetaSeries.Version.ApiMethod(false, "shows/remove")]
        public void DelShow(IShow show)
        {
            DelShow(show.Url);
        }

        /// <summary>
        /// Mark the episode as watched for the member
        /// </summary>
        /// <param name="episode">episode watched</param>
        /// <param name="note">note of the episode (default = note)</param>
        [ApiBetaSeries.Version.ApiMethod(false, "members/watched")]
        public void SetWatched(IEpisode episode, int note = -1)
        {
            episode.SetWatched(this, note);
        }


        /// <summary>
        /// Mark the episode as watched for the member
        /// </summary>
        /// <param name="urlShow">url of the show</param>
        /// <param name="season">season of the episode</param>
        /// <param name="episode">episode number</param>
        /// <param name="note">note of the episode (default = note)</param>
        [ApiBetaSeries.Version.ApiMethod(false, "members/watched")]
        public void SetWatched(string urlShow, int season, int episode, int note = -1)
        {
            IEpisode ep = EpisodeFactory.CreateEpisode();
            ep.UrlShow = urlShow;
            ep.Season = season;
            ep.Number = episode;
            SetWatched(ep, note);
        }

        /// <summary>
        /// Mark the episode as downloaded
        /// </summary>
        /// <param name="episode">episode downloaded</param>
        /// <param name="token">token of the member</param>
        /// <returns>if the episode is downloaded</returns>
        [ApiBetaSeries.Version.ApiMethod(false, "members/downloaded")]
        public bool SetDownloaded(IEpisode episode)
        {
            return episode.SetDownloaded(this);
        }

        /// <summary>
        /// Mark the episode as downloaded for the member
        /// </summary>
        /// <param name="urlShow">url of the show</param>
        /// <param name="season">season of the episode</param>
        /// <param name="episode">episode number</param>
        /// <returns>if the episode is downloaded</returns>
        [ApiBetaSeries.Version.ApiMethod(false, "members/downloaded")]
        public bool SetDownloaded(string urlShow, int season, int episode)
        {
            IEpisode ep = EpisodeFactory.CreateEpisode();
            ep.UrlShow = urlShow;
            ep.Season = season;
            ep.Number = episode;
            return SetDownloaded(ep);
        }

        /// <summary>
        /// Destroy a token
        /// Disconnect the member
        /// </summary>
        [ApiBetaSeries.Version.ApiMethod(true, "members/destroy")]
        public void Destroy()
        {
            XmlDocument doc = Utils.UtilsXML.GetUrl("/members/destroy.xml", "&token=" + Token);
        }

        /// <summary>
        /// allow to know if a token is still active
        /// and member still connected
        /// </summary>
        /// <returns>if the token is active</returns>
        [ApiBetaSeries.Version.ApiMethod(true, "members/is_active")]
        public bool IsActive()
        {
            try
            {
                XmlDocument doc = Utils.UtilsXML.GetUrl("/members/is_active.xml", "&token=" + Token);
            }
            catch (BetaSeriesException ex)
            {
                if (ex.ErrorNumber == 2001)
                {
                    return false;
                }
                else
                {
                    throw ex;
                }
            }
            return true;
        }

        /// <summary>
        /// Get or update all info of an identified member
        /// </summary>
        /// <returns>The user information</returns>
        [ApiBetaSeries.Version.ApiMethod(true, "members/infos")]
        public void GetInfos()
        {
            int since = -1;
            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            TimeSpan unixtime = this.Cached - origin;
            since = (int)unixtime.TotalSeconds;
            XmlDocument doc = Utils.UtilsXML.GetUrl("/members/infos.xml", "&token=" + Token + (since > 0 ? "&since=" + since : ""));
            XmlElement xe = doc["root"]["member"];

            if (xe == null)
            {
                throw new BetaSeriesException(1003, "Invalid action.");
            }
            UserFactory.CreateUser(this, xe);
        }




        /// <summary>
        /// get infos of a member for this authentified member 
        /// (when privacy policy = friend only for exemple)
        /// </summary>
        /// <param name="username">username of the member</param>
        /// <returns>info of the member</returns>
        [ApiBetaSeries.Version.ApiMethod(true, "members/infos")]
        public IUser GetInfosOfMember(string username)
        {
            IUser user = UserFactory.CreateUser();
            XmlDocument doc = Utils.UtilsXML.GetUrl("/members/infos/" + username + ".xml", (!string.IsNullOrEmpty(Token) ? "&token=" + Token : ""));
            XmlElement xe = doc["root"]["member"];

            if (xe == null)
            {
                throw new BetaSeriesException(1003, "Invalid action.");
            }

            UserFactory.CreateUser(user, xe);
            user.Token = null;

            return user;
        }

        /// <summary>
        /// Get planning of identified member
        /// </summary>
        /// <param name="unseen">show only unseen episodes</param>
        /// <returns>list of episodes</returns>
        [ApiBetaSeries.Version.ApiMethod(true, "planning/member")]
        public List<IEpisode> GetPlanning(bool unseen = false)
        {
            List<IEpisode> list = new List<IEpisode>();
            XmlDocument doc = Utils.UtilsXML.GetUrl("/planning/member.xml", "&token=" + Token + (unseen ? "&view=unseen" : ""));
            XmlElement xe = doc["root"]["planning"];

            if (xe == null)
            {
                throw new BetaSeriesException(1003, "Invalid action.");
            }
            foreach (XmlElement e in xe.GetElementsByTagName("episode"))
            {
                IEpisode episode = EpisodeFactory.CreateEpisode(e);
                list.Add(episode);
            }
            return list;
        }

        /// <summary>
        /// Get the username of all the friend of the member
        /// </summary>
        /// <returns></returns>
        [ApiBetaSeries.Version.ApiMethod(true, "members/friends")]
        public List<string> GetFriends()
        {
            List<string> list = new List<string>();
            string url = null;
            string param = null;
            if (string.IsNullOrEmpty(this.Token))
            {
                url = string.Format("/members/friends/{0}.xml", this.Login);
                param = "";
            }
            else
            {
                url = "/members/friends.xml";
                param = string.Format("&token={0}", this.Token);
            }

            XmlDocument doc = Utils.UtilsXML.GetUrl(url, param);
            XmlElement xe = doc["root"]["friends"];

            if (xe == null)
            {
                throw new BetaSeriesException(1003, "Invalid action.");
            }

            foreach (XmlElement e in xe.GetElementsByTagName("friend"))
            {
                list.Add(e.FirstChild.Value);
            }


            return list;
        }

        /// <summary>
        /// Defined a mark for this episode
        /// </summary>
        /// <param name="episode">episode to mark</param>
        /// <param name="mark">mark of this episode</param>
        [ApiBetaSeries.Version.ApiMethod(false, "members/note")]
        public void SetMark(IEpisode episode, int mark)
        {
            episode.SetMark(this, mark);
        }

        /// <summary>
        /// Defined a mark for this episode
        /// </summary>
        /// <param name="urlShow">url of the show</param>
        /// <param name="season">season number</param>
        /// <param name="episode">episode number</param>
        /// <param name="mark">mark of this episode</param>
        [ApiBetaSeries.Version.ApiMethod(false, "members/note")]
        public void SetMark(string urlShow, int season, int episode, int mark)
        {
            IEpisode ep = EpisodeFactory.CreateEpisode();
            ep.UrlShow = urlShow;
            ep.Season = season;
            ep.Number = episode;
            SetMark(ep, mark);
        }

        /// <summary>
        /// Recommend this show to a friend
        /// </summary>
        /// <param name="show">Show you recommend</param>
        /// <param name="friend">friend's username</param>
        [ApiBetaSeries.Version.ApiMethod(false, "shows/recommend")]
        public void Recommend(IShow show, string friend)
        {
            show.Recommend(this, friend);
        }
        /// <summary>
        /// Recommend this show to a friend
        /// </summary>
        /// <param name="urlShow">url of the show</param>
        /// <param name="friend">friend's username</param>
        [ApiBetaSeries.Version.ApiMethod(false, "shows/recommend")]
        public void Recommend(string urlShow, string friend)
        {
            IShow show = ShowFactory.CreateShow();
            show.Url = urlShow;
            Recommend(show, friend);
        }
        #endregion Methods

        /// <summary>
        /// Represent a show in request members/infos
        /// </summary>
        public class Show : ApiBetaSeries.IShowUser
        {
            /// <summary>
            /// Url of the show
            /// </summary>
            public string Url { get; set; }
            /// <summary>
            /// Title of the show
            /// </summary>
            public string Title { get; set; }
            /// <summary>
            /// show archived or not
            /// </summary>
            public bool Archived { get; set; }
        }
    }
    
}
